package com.millstein.tsinglog.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.millstein.tsinglog.commons.constant.SystemConstant;
import com.millstein.tsinglog.commons.enums.ResultCode;
import com.millstein.tsinglog.exception.TsinglogException;
import com.millstein.tsinglog.commons.service.BaseService;
import com.millstein.tsinglog.mapper.biz.ArticleMapper;
import com.millstein.tsinglog.mapper.biz.CommentMapper;
import com.millstein.tsinglog.mapper.biz.UserMapper;
import com.millstein.tsinglog.utils.*;
import com.millstein.tsinglog.model.dto.comment.BatchCommentCheckDTO;
import com.millstein.tsinglog.model.dto.comment.CommentCheckDTO;
import com.millstein.tsinglog.model.dto.comment.CommentSaveDTO;
import com.millstein.tsinglog.model.pojo.Article;
import com.millstein.tsinglog.model.pojo.Comment;
import com.millstein.tsinglog.model.pojo.User;
import com.millstein.tsinglog.model.vo.biz.CommentVo;
import com.millstein.tsinglog.model.vo.result.PageDetail;
import com.millstein.tsinglog.model.vo.result.PageResult;
import com.millstein.tsinglog.service.CommentService;
import com.millstein.tsinglog.service.MailService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class CommentServiceImpl extends BaseService implements CommentService {


    @Autowired
    private CommentMapper commentMapper;

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MailService mailService;

    @Override
    public PageResult<CommentVo> getCommentByPage(Integer currentPage,
                                                  Integer pageSize,
                                                  String ip,
                                                  String keyword,
                                                  Short commentStatus) {
        // 1.处理参数
        if (currentPage == null) {
            currentPage = SystemConstant.DEFAULT_CURRENT_PAGE;
        }
        if (pageSize == null) {
            pageSize = SystemConstant.DEFAULT_PAGE_SIZE;
        }

        // 2.条件查询
        Example example = new Example(Comment.class);
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotBlank(ip)) {
            criteria.andLike("authorIp", "%" + ip + "%");
        }
        if (StringUtils.isNotBlank(keyword)) {
            criteria.andLike("commentContent", "%" + keyword + "%");
        }
        if (commentStatus != null) {
            criteria.andEqualTo("commentStatus", commentStatus);
        }

        // 3.只查询状态为正常的评论
        criteria.andEqualTo("status", SystemConstant.STATUS_ENTITY_NORMAL);

        // 4.排序
        example.setOrderByClause("comment_status asc, update_time desc");

        // 5.查询
        PageHelper.startPage(currentPage, pageSize);
        List<Comment> list = commentMapper.selectByExample(example);
        PageInfo pageInfo = new PageInfo(list);

        // 6.将List<Comment>转为List<CommentVo>
        List<CommentVo> listVo = this.transListCommentToListCommentVo(list, true);

        return new PageResult<CommentVo>(pageInfo.getTotal(), pageInfo.getPages(), listVo);
    }

    /**
     * 将List<Comment>转为List<CommentVo>
     * @param list 需要转化的List<Comment>
     * @param isForServer 是否为后台提供数据，true为是，false为前台提供数据
     * @return
     */
    private List<CommentVo> transListCommentToListCommentVo(List<Comment> list, Boolean isForServer) {
        List<CommentVo> listVo = new ArrayList<CommentVo>();
        for (Comment comment: list) {
            CommentVo vo = new CommentVo(comment);
            // 1.完善vo中的article字段
            Article article;
            Long aid = vo.getAid();
            if (isForServer) {
                article = articleMapper.selectArticleByPrimaryKeyForServer(aid);
            } else {
                article = articleMapper.selectArticleByPrimaryKeyForWeb(aid);
            }

            if (vo.getAid() == 0L) {
                vo.setArticle("留言板");
            } else {
                if (article == null) {
                    throw new TsinglogException(ResultCode.HANDLE_DATA_NOT_EXISTED);
                }
                vo.setArticle(article.getTitle());
            }

            // 2.完善vo中有关preComment字段
            if (vo.getPreCommentId() != null) {
                Comment preComment;
//                if (isForServer) {
//                    preComment = commentMapper.selectCommentByPrimaryKeyForServer(vo.getPreCommentId());
//                } else {
//                    preComment = commentMapper.selectCommentByPrimaryKeyForWeb(vo.getPreCommentId());
//                }
                preComment = commentMapper.selectByPrimaryKey(vo.getPreCommentId());

                if (preComment == null) {
                    throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
                }
                // 如果查出来的该条评论已经被删除
                if (SystemConstant.STATUS_ENTITY_DELETE.equals(preComment.getStatus())
                        || preComment.getCommentStatus() != SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS) {
                    vo.setPreComment("原评论已被删除");
                } else {
                    vo.setPreComment(preComment.getCommentContent());
                }
                vo.setPreAuthorName(preComment.getAuthorName());
                vo.setPreCreateTime(preComment.getCreateTime());
                vo.setPreFloor(preComment.getFloor());
                vo.setPreStatus(preComment.getStatus());
            }
            // 3.完善vo中roleName字段
            if ("超级管理员".equals(comment.getAuthorName())) {
                vo.setRoleName("超级管理员");
            } else if ("管理员".equals(comment.getAuthorName())) {
                vo.setRoleName("普通管理员");
            }
            listVo.add(vo);
        }
        return listVo;
    }

    /**
     * 审核评论，逻辑：
     * 1.如果是管理员发的评论无需审核直接通过
     * 2.其他用户发的评论需要经过审核，如果审核通过时并且对该评论进行了回复，需要以管理员的身份多添加一条评论的记录
     * @param commentCheckDTO 评论的信息
     * @param uid 审核用户的id
     */
    @Override
    @Transactional
    public void checkComment(CommentCheckDTO commentCheckDTO, Long uid) {

        User user = userMapper.selectUserByPrimaryKey(uid);
        // 1.查询原评论的信息
        Comment dbComment = commentMapper.selectCommentByPrimaryKeyForServer(commentCheckDTO.getCommentId());
        if (dbComment == null) {
            throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
        }

        if (commentCheckDTO.getCheckResult() == SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS) {
            // 2.审核通过
            dbComment.setCommentStatus(SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS);
            dbComment.setCheckTime(getCurrentDateStr());
            dbComment.setUpdateTime(getCurrentDateStr());

            // 获取本文章的已有的评论数
            int totalCommentForAid = commentMapper.getCommentByAid(dbComment.getAid());
            dbComment.setFloor(totalCommentForAid + 1);

            int result = commentMapper.updateByPrimaryKey(dbComment);
            if (result != 1) {
                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
            }
            // 如果管理员在审核的时候同时给评论进行了回复的情况
            if (StringUtils.isNotBlank(commentCheckDTO.getCommentContent())) {
                this.insertCommentForAdmin(dbComment.getAid(), commentCheckDTO, user);
            }

        } else {
            // 3.审核失败
            dbComment.setCommentStatus(SystemConstant.COMMENT_HAS_BEEN_AUDITED_FAILURE);
            dbComment.setCheckTime(getCurrentDateStr());
            dbComment.setUpdateTime(getCurrentDateStr());
            dbComment.setFailureInfo(commentCheckDTO.getFailureInfo());

            int result = commentMapper.updateByPrimaryKey(dbComment);
            if (result != 1) {
                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
            }
        }

        // 发送邮件
        if (commentCheckDTO.getNeedSendMail()) {
            new Thread(() -> sendMailForComment(dbComment, user)).start();
        }
    }

    /**
     * 根据评论信息发送相应的邮件
     * @param comment 评论信息
     * @param user 用户信息
     */
    private void sendMailForComment(Comment comment, User user) {
        String link;
        String articleTitle;
        // 留言板
        if (0L == comment.getAid()) {
            link = "http://www.tsinglog.online/guestBook";
            articleTitle = "留言板";
        } else {
            Article dbArticle = articleMapper.selectArticleByPrimaryKeyForServer(comment.getAid());
            link = "http://www.tsinglog.online/article/" + comment.getAid();
            articleTitle = dbArticle.getTitle();
        }

        String to = comment.getEmail();
        String subject = "tsinglog邮件--评论审核结果";
        StringBuilder sb = new StringBuilder();

        if (SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS == comment.getCommentStatus()) {
            sb.append("<br/><b>评论审核结果</b>")
                    .append("<p>您发表的评论：</p>")
                    .append("<div style='display: block; background-color: #ccc; padding: 5px 10px'; margin-left: " +
                            "10px>")
                    .append(comment.getCommentContent())
                    .append("</div>")
                    .append("<p>审核已经通过，快来看看吧</p>")
                    .append("<p>原链接：")
                    .append("<a href=" + link + ">")
                    .append(articleTitle + "</a>")
                    .append("</p>");
        } else {
            sb.append("<br/><b>评论审核结果</b>")
                    .append("<p>您发表的评论：</p>")
                    .append("<div style='display: block; background-color: #ccc; padding: 5px 10px'; margin-left: " +
                            "10px>")
                    .append(comment.getCommentContent())
                    .append("</div>")
                    .append("<p>审核没有通过，原因：<b>")
                    .append(comment.getFailureInfo())
                    .append("</b></p>")
                    .append("<p>原链接：")
                    .append("<a href=" + link + ">")
                    .append(articleTitle + "</a>")
                    .append("</p>");
        }
        mailService.sendMail(to, subject, sb.toString(), user);
    }

    /**
     * 为管理员新增评论
     * @param aid 该评论所对应的文章
     * @param commentCheckDTO 评论的信息
     * @param user 审核员的信息
     */
    public void insertCommentForAdmin(Long aid, CommentCheckDTO commentCheckDTO, User user) {
        // 为了防止管理员的回复在显示的时候跑到原评论的下面，所以在这里执行的时候人为加上1秒作为新评论的回复时间
        String newDateStr = getCurrentDateStr(1000);

        Comment comment = new Comment();
        comment.setAuthorName(user.getNickname());
        comment.setAuthorPhoto(user.getAvatar());
        comment.setAuthorIp(IpUtils.getRealIp(RequestUtils.getRequest()));
        comment.setLocation(AddressUtils.setLocationByIpAddress(IpUtils.getRealIp(RequestUtils.getRequest())));
        comment.setEmail(SystemConstant.ADMIN_EMAIL);
        comment.setAid(aid);
        comment.setCommentContent(commentCheckDTO.getCommentContent());
        comment.setPreCommentId(commentCheckDTO.getCommentId());
        comment.setSupportCount(SystemConstant.DEFAULT_SUPPORT_COUNT);
        comment.setOpposeCount(SystemConstant.DEFAULT_OPPOSE_COUNT);
        comment.setCommentStatus(SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS);
        comment.setCheckTime(newDateStr);
        comment.setStatus(SystemConstant.STATUS_ENTITY_NORMAL);
        comment.setCreateTime(newDateStr);
        comment.setUpdateTime(newDateStr);

        // 获取本文章的已有的评论数
        int totalCommentForAid = commentMapper.getCommentByAid(aid);
        comment.setFloor(totalCommentForAid + 1);

        int result = commentMapper.insert(comment);
        if (result != 1) {
            throw new TsinglogException(ResultCode.INSERT_FAILURE);
        }
    }



    @Override
    public CommentVo getCommentById(Long id) {
        if (id == null) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Comment dbComment = commentMapper.selectCommentByPrimaryKeyForServer(id);
        if (dbComment == null) {
            throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
        }

        CommentVo vo = new CommentVo(dbComment);

        List<Comment> list = this.getNextComment(dbComment.getId());
        // 只有该条评论被管理员回复时，才需要设置adminReply的值。有comment:check权限的都属于管理员
        // 获取所有拥有comment:check权限的用户发表的评论
        List<Comment> nextComments = this.getCommentByPermissionFromList(list, "comment:check");
        if (!CollectionUtils.isEmpty(nextComments)) {
            // 管理员对某个用户评论的回复只有一条
            Comment adminComment = nextComments.get(0);
            vo.setAdminReply(adminComment.getCommentContent());
        }

        return vo;
    }

    /**
     * 修改审核信息的方法，逻辑：
     * 1.对管理员的评论进行修改：管理员是不需要进行审核的，所以不能直接对管理员的评论进行修改
     * 2.对普通用户的评论进行修改：
     *  2.1.将结果从审核通过变为审核失败，如果管理员做过评论，那么要将该条评论删除
     *  2.2.将结果从审核失败变为审核通过，清空失败原因，如果管理员要做回复，那么还要新增对应的评论
     *  2.3.结果是审核失败不变，修改对应的失败信息
     *  2.4.结果是审核通过不变，则按下面逻辑进行：
     *      2.4.1.如果原来管理员没有进行回复现在进行回复了，则需要新增一条评论；
     *      2.4.2.如果原来管理员进行过回复现在要修改，则需要对管理员原来的那条评论进行修改
     *      2.4.3.如果原来管理员进行过回复现在要删除，则需要将原来管理员的那条评论进行删除
     * @param commentCheckDTO
     * @param uid
     */
    @Override
    @Transactional
    public void modifyCheck(CommentCheckDTO commentCheckDTO, Long uid) {
        User user = userMapper.selectUserByPrimaryKey(uid);
        Long id = commentCheckDTO.getCommentId();
        if (id == null) {
            throw new TsinglogException(ResultCode.PARAM_NOT_COMPLETE);
        }

        Comment dbComment = commentMapper.selectCommentByPrimaryKeyForServer(id);
        if (dbComment == null) {
            throw new TsinglogException(ResultCode.HANDLE_DATA_NOT_EXISTED);
        }
        // 1.如果要修改的该条评论的作者拥有comment:check权限，那么不允许修改
        // 根据code查询所有拥有此权限的用户的username
        List<String> names = userMapper.selectUserByCode("comment:check");
        if (names.contains(dbComment.getAuthorName())) {
            throw new TsinglogException(ResultCode.PARAM_IS_INVALID);
        }

        // 2.对普通用户评论的修改
        if (SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS.equals(dbComment.getCommentStatus()) &&
                SystemConstant.COMMENT_HAS_BEEN_AUDITED_FAILURE.equals(commentCheckDTO.getCheckResult())) {
            // 2.1.将结果从审核通过变为审核失败，
            dbComment.setCommentStatus(commentCheckDTO.getCheckResult());
            dbComment.setUpdateTime(getCurrentDateStr());
            dbComment.setCheckTime(getCurrentDateStr());
            dbComment.setFailureInfo(commentCheckDTO.getFailureInfo());

            int result = commentMapper.updateByPrimaryKey(dbComment);
            if (result != 1) {
                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
            }

            // 如果管理员做过评论，那么要将该条评论删除
            List<Comment> list = this.getNextComment(dbComment.getId());
            List<Comment> nextComments = this.getCommentByPermissionFromList(list, "comment:check");
            if (!CollectionUtils.isEmpty(nextComments)) {
                // 管理员对某个用户的回复只有一条
                Comment adminComment = nextComments.get(0);
                adminComment.setStatus(SystemConstant.STATUS_ENTITY_DELETE);
                adminComment.setUpdateTime(getCurrentDateStr());
                result = commentMapper.updateByPrimaryKey(adminComment);
                if (result != 1) {
                    throw new TsinglogException(ResultCode.UPDATE_FAILURE);
                }

            }

            // 只有当审核结果发生了改变，才会去发送邮件
            if (commentCheckDTO.getNeedSendMail()) {
                this.sendMailForComment(dbComment, user);
            }

        } else if (SystemConstant.COMMENT_HAS_BEEN_AUDITED_FAILURE.equals(dbComment.getCommentStatus()) &&
                SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS.equals(commentCheckDTO.getCheckResult())) {
            // 2.2.将结果从审核失败变为审核通过，清空失败原因，
            dbComment.setCommentStatus(commentCheckDTO.getCheckResult());
            dbComment.setUpdateTime(getCurrentDateStr());
            dbComment.setCheckTime(getCurrentDateStr());
            dbComment.setFailureInfo("");

            int result = commentMapper.updateByPrimaryKey(dbComment);
            if (result != 1) {
                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
            }
            // 如果管理员要做回复，那么还要新增对应的评论
            if (StringUtils.isNotBlank(commentCheckDTO.getCommentContent())) {

                this.insertCommentForAdmin(dbComment.getAid(), commentCheckDTO, user);
            }

            // 只有当审核结果发生了改变，才会去发送邮件
            if (commentCheckDTO.getNeedSendMail()) {
                this.sendMailForComment(dbComment, user);
            }

        } else if (SystemConstant.COMMENT_HAS_BEEN_AUDITED_FAILURE.equals(dbComment.getCommentStatus()) &&
                SystemConstant.COMMENT_HAS_BEEN_AUDITED_FAILURE.equals(commentCheckDTO.getCheckResult())) {
            // 2.3.结果是审核失败不变，修改对应的失败信息
            dbComment.setUpdateTime(getCurrentDateStr());
            dbComment.setFailureInfo(commentCheckDTO.getFailureInfo());

            int result = commentMapper.updateByPrimaryKey(dbComment);
            if (result != 1) {
                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
            }
        } else {
            // 2.4.结果是审核通过不变
//            dbComment.setUpdateTime(getCurrentDateStr());
//
//            int result = commentMapper.updateByPrimaryKey(dbComment);
//            if (result != 1) {
//                throw new TsinglogException(ResultCode.UPDATE_FAILURE);
//            }

            List<Comment> list = this.getNextComment(dbComment.getId());
            List<Comment> nextComments = this.getCommentByPermissionFromList(list, "comment:check");

            if (CollectionUtils.isEmpty(nextComments) && StringUtils.isNotBlank(commentCheckDTO.getCommentContent())) {
                // 2.4.1.如果原来管理员没有进行回复现在进行回复了，则需要新增一条评论；
                this.insertCommentForAdmin(dbComment.getAid(), commentCheckDTO, user);
            } else if (!CollectionUtils.isEmpty(nextComments) && StringUtils.isNotBlank(commentCheckDTO.getCommentContent())) {
                // 2.4.2.如果原来管理员进行过回复现在要修改，则需要对管理员原来的那条评论进行修改
                Comment adminComment = nextComments.get(0);
                adminComment.setCommentContent(commentCheckDTO.getCommentContent());
                adminComment.setUpdateTime(getCurrentDateStr(1000));

                int result = commentMapper.updateByPrimaryKey(adminComment);
                if (result != 1) {
                    throw new TsinglogException(ResultCode.UPDATE_FAILURE);
                }

            } else if (!CollectionUtils.isEmpty(nextComments) && StringUtils.isBlank(commentCheckDTO.getCommentContent())) {
                // 2.4.3.如果原来管理员进行过回复现在要删除，则需要将原来管理员的那条评论进行删除
                Comment adminComment = nextComments.get(0);
                adminComment.setStatus(SystemConstant.STATUS_ENTITY_DELETE);
                adminComment.setUpdateTime(getCurrentDateStr(1000));

                int result = commentMapper.updateByPrimaryKey(adminComment);
                if (result != 1) {
                    throw new TsinglogException(ResultCode.UPDATE_FAILURE);
                }
            }
        }


    }

    /**
     * 批量审核评论，逻辑：
     * 批量审核评论时，如果是审核通过的话是不能添加回复信息的；审核失败时可以添加失败信息
     * @param batchCommentCheckDTO 审核的信息
     */
    @Override
    @Transactional
    public void batchCheckComment(BatchCommentCheckDTO batchCommentCheckDTO, Long uid) {

        List<Long> ids = batchCommentCheckDTO.getCommentIds();
        if (CollectionUtils.isEmpty(ids)) {
            throw new TsinglogException(ResultCode.PARAM_NOT_COMPLETE);
        }

        for (Long id: ids) {
            CommentCheckDTO dto = new CommentCheckDTO();
            dto.setCheckResult(batchCommentCheckDTO.getCheckResult());
            dto.setCommentId(id);
            dto.setFailureInfo(batchCommentCheckDTO.getFailureInfo());

            this.checkComment(dto, uid);
        }
    }

    /**
     * 删除评论
     * @param ids 评论id集合
     */
    @Override
    public void deleteByIds(List<Long> ids) {

        if (CollectionUtils.isEmpty(ids)) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        for (Long id: ids) {
            Comment dbComment = commentMapper.selectCommentByPrimaryKeyForServer(id);
            if (dbComment == null) {
                throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
            }
            dbComment.setStatus(SystemConstant.STATUS_ENTITY_DELETE);
            dbComment.setUpdateTime(getCurrentDateStr());

            commentMapper.updateByPrimaryKeySelective(dbComment);
        }
    }

    @Override
    public PageDetail<CommentVo> getCommentByAidForPortal(Long aid, Integer currentPage) {

        if (currentPage == null) {
            currentPage = SystemConstant.DEFAULT_CURRENT_PAGE;
        }

        Example example = new Example(Comment.class);
        Example.Criteria criteria = example.createCriteria();

        criteria.andEqualTo("aid", aid);
        criteria.andEqualTo("commentStatus", SystemConstant.COMMENT_HAS_BEEN_AUDITED_PASS);
        criteria.andEqualTo("status", SystemConstant.STATUS_ENTITY_NORMAL);

        example.setOrderByClause("floor desc");

        PageHelper.startPage(currentPage, SystemConstant.DEFAULT_PAGE_SIZE_FOR_LIST);
        List<Comment> comments = commentMapper.selectByExample(example);
        List<CommentVo> voList = new ArrayList<>();

        // 将List<Comment>转为ListVo<Comment>
        if (!CollectionUtils.isEmpty(comments)) {
            voList = this.transListCommentToListCommentVo(comments, false);
        }

        PageInfo pageInfo = new PageInfo(comments, 5);

        return new PageDetail<CommentVo>(pageInfo, voList, new ArrayList());
    }

    @Override
    public Integer doWithComment(Long cid, Long aid, Short type) {

        String supportKey =
                IpUtils.getRealIp(RequestUtils.getRequest()) + SystemConstant.REDIS_PREFIX_FOR_DO_SUPPORT + cid;
        String opposeKey =
                IpUtils.getRealIp(RequestUtils.getRequest()) + SystemConstant.REDIS_PREFIX_FOR_DO_OPPOSE + cid;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();

        if (type == SystemConstant.TYPE_FOR_DO_SUPPORT_FOR_COMMENT) {
            if (redisTemplate.hasKey(supportKey)) {
                throw new TsinglogException(ResultCode.DO_SUPPORT_TOO_OFTEN);
            }
        } else if (type == SystemConstant.TYPE_FOR_DO_OPPOSE_FOR_COMMENT) {
            if (redisTemplate.hasKey(opposeKey)) {
                throw new TsinglogException(ResultCode.DO_OPPOSE_TOO_OFTEN);
            }
        } else {
            throw new TsinglogException(ResultCode.PARAM_IS_INVALID);
        }

        if (cid == null || aid == null || type == null) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Comment dbComment = commentMapper.selectCommentByPrimaryKeyForWeb(cid);
        if (dbComment == null) {
            throw new TsinglogException(ResultCode.HANDLE_DATA_NOT_EXISTED);
        }
        if (dbComment.getAid() != aid) {
            throw new TsinglogException(ResultCode.PARAM_IS_INVALID);
        }

        if (type == SystemConstant.TYPE_FOR_DO_SUPPORT_FOR_COMMENT) {
            dbComment.setSupportCount(dbComment.getSupportCount() + 1);
        } else {
            dbComment.setOpposeCount(dbComment.getOpposeCount() + 1);
        }

        dbComment.setUpdateTime(getCurrentDateStr());

        int result = commentMapper.updateByPrimaryKey(dbComment);
        if (result != 1) {
            throw new TsinglogException(ResultCode.PORTAL_OPERATION_OCCURS_ERROR);
        }

        // 将操作的id放到redis中
        if (type == SystemConstant.TYPE_FOR_DO_SUPPORT_FOR_COMMENT) {
            operations.set(supportKey, cid, 1, TimeUnit.HOURS);
            return dbComment.getSupportCount();
        } else {
            operations.set(opposeKey, cid, 1, TimeUnit.HOURS);
            return dbComment.getOpposeCount();
        }
    }

    @Override
    public void saveComment(CommentSaveDTO commentSaveDTO) {

        Long aid = commentSaveDTO.getAid();

        // 1.防止已经在后台修改为不可评论的文章，在修改前已经打开页面仍然可以被评论的问题。如果是留言板，自然不需要做这个判断
        if (aid != 0L) {
            Article dbArticle = articleMapper.selectArticleByPrimaryKeyForWeb(aid);
            if (dbArticle == null || dbArticle.getCanComment() == SystemConstant.ARTICLE_CANNOT_COMMENT) {
                throw new TsinglogException(ResultCode.ARTICLE_STATUS_ERROR);
            }
        }

        String key = IpUtils.getRealIp(RequestUtils.getRequest())
                + SystemConstant.REDIS_PREFIX_FOR_SUBMIT_COMMENT + aid;

        if (redisTemplate.hasKey(key)) {
            if (aid == 0L) {
                throw new TsinglogException(ResultCode.SUBMIT_GUEST_BOOK_TOO_OFTEN);
            }
            throw new TsinglogException(ResultCode.SUBMIT_COMMENT_TOO_OFTEN);
        }

        Comment comment = new Comment();
        comment.setAuthorName(commentSaveDTO.getName());
        // TODO 现阶段头像使用系统自带的头像，头像的分配采用随机分配，暂定20个头像
        // 生成1~20的随机数
        int random = MyUtils.getRandomNum(1, 20);
        comment.setAuthorPhoto(random + ".jpg");
        comment.setAuthorIp(IpUtils.getRealIp(RequestUtils.getRequest()));
        comment.setLocation(AddressUtils.setLocationByIpAddress(IpUtils.getRealIp(RequestUtils.getRequest())));
        comment.setEmail(commentSaveDTO.getEmail());
        comment.setAid(commentSaveDTO.getAid());
        comment.setCommentContent(commentSaveDTO.getContent());
        comment.setSupportCount(SystemConstant.DEFAULT_SUPPORT_COUNT);
        comment.setOpposeCount(SystemConstant.DEFAULT_OPPOSE_COUNT);
        comment.setCommentStatus(SystemConstant.COMMENT_HAS_NOT_BEEN_AUDITED);
        comment.setCheckTime(SystemConstant.DEFAULT_COMMENT_TIME);
        comment.setStatus(SystemConstant.STATUS_ENTITY_NORMAL);
        comment.setCreateTime(getCurrentDateStr());
        comment.setUpdateTime(getCurrentDateStr());
        if (commentSaveDTO.getPreFloor() != null) {
            Long preId = commentMapper.getIdByFloor(commentSaveDTO.getPreFloor());
            comment.setPreCommentId(preId);
        }

        int result = commentMapper.insert(comment);
        if (result != 1) {
            throw new TsinglogException(ResultCode.INSERT_COMMENT_ERROR);
        }

        // 将发表评论的信息放在redis中，限制其一分钟才能发表一次
        redisTemplate.opsForValue().set(key, commentSaveDTO.getAid(), 1, TimeUnit.MINUTES);
    }


    /**
     * 根据用户名查找在评论集合list中该用户所发表的评论
     * @param list 查找的集合
     * @param code 权限对应的代码
     * @return
     */
    private List<Comment> getCommentByPermissionFromList(List<Comment> list, String code) {
        // 根据code查询所有拥有此权限的用户的username
        List<String> names = userMapper.selectUserByCode(code);
        List<Comment> returnList = new ArrayList<Comment>();
        if (!CollectionUtils.isEmpty(list)) {
            for (Comment comment: list) {
                if (names.contains(comment.getAuthorName())) {
                    returnList.add(comment);
                }
            }
        }
        return returnList;
    }

    /**
     * 查询对指定评论进行回复的评论集合（结果是status为正常的评论）
     * @param commentId 指定评论的id
     * @return
     */
    private List<Comment> getNextComment(Long commentId) {
        Comment nextComment = new Comment();
        nextComment.setPreCommentId(commentId);
        nextComment.setStatus(SystemConstant.STATUS_ENTITY_NORMAL);
        return commentMapper.select(nextComment);
    }

}
